from pyjvm.exceptions import NullPointException, IndexOutOfBoundsException
from pyjvm.instructions.common import Instruction, Index8Instruction
from pyjvm.runtime.thread import Frame


def load_inst_type(name: str, index):
    def execute(self, frame: Frame):
        val = frame.local_vars[index]
        frame.operand_stack.push(val)

    return type(name, (Instruction,), dict(execute=execute))


class ARRAY_LOAD(Instruction):
    def execute(self, frame: Frame):
        stack = frame.operand_stack
        index = stack.pop()
        arr = stack.pop()

        if arr is None:
            raise NullPointException()
        if index < 0 or index >= len(arr.data):
            raise IndexOutOfBoundsException()

        item = arr.data[index]
        stack.push(item)


class LOAD(Index8Instruction):

    def execute(self, frame: Frame):
        val = frame.local_vars[self.index]
        frame.operand_stack.push(val)


class ILOAD(LOAD):
    pass


class LLOAD(LOAD):
    pass


class FLOAD(LOAD):
    pass


class DLOAD(LOAD):
    pass


class ALOAD(LOAD):
    pass


class IALOAD(ARRAY_LOAD):
    pass


class BALOAD(ARRAY_LOAD):
    pass


class AALOAD(ARRAY_LOAD):
    pass


class CALOAD(ARRAY_LOAD):
    pass


class DALOAD(ARRAY_LOAD):
    pass


class FALOAD(ARRAY_LOAD):
    pass


class LALOAD(ARRAY_LOAD):
    pass


class SALOAD(ARRAY_LOAD):
    pass


ILOAD_0 = load_inst_type('ILOAD_0', 0)
ILOAD_1 = load_inst_type('ILOAD_1', 1)
ILOAD_2 = load_inst_type('ILOAD_2', 2)
ILOAD_3 = load_inst_type('ILOAD_3', 3)

LLOAD_0 = load_inst_type('LLOAD_0', 0)
LLOAD_1 = load_inst_type('LLOAD_1', 1)
LLOAD_2 = load_inst_type('LLOAD_2', 2)
LLOAD_3 = load_inst_type('LLOAD_3', 3)

FLOAD_0 = load_inst_type('FLOAD_0', 0)
FLOAD_1 = load_inst_type('FLOAD_1', 1)
FLOAD_2 = load_inst_type('FLOAD_2', 2)
FLOAD_3 = load_inst_type('FLOAD_3', 3)

DLOAD_0 = load_inst_type('DLOAD_0', 0)
DLOAD_1 = load_inst_type('DLOAD_1', 1)
DLOAD_2 = load_inst_type('DLOAD_2', 2)
DLOAD_3 = load_inst_type('DLOAD_3', 3)

ALOAD_0 = load_inst_type('ALOAD_0', 0)
ALOAD_1 = load_inst_type('ALOAD_1', 1)
ALOAD_2 = load_inst_type('ALOAD_2', 2)
ALOAD_3 = load_inst_type('ALOAD_3', 3)

__all__ = ['ILOAD', 'LLOAD', 'FLOAD', 'DLOAD', 'ALOAD', 'ILOAD_0',
           'ILOAD_1', 'ILOAD_2', 'ILOAD_3', 'LLOAD_0', 'LLOAD_1',
           'LLOAD_2', 'LLOAD_3', 'FLOAD_0', 'FLOAD_1', 'FLOAD_2',
           'FLOAD_3', 'DLOAD_0', 'DLOAD_1', 'DLOAD_2', 'DLOAD_3',
           'ALOAD_0', 'ALOAD_1', 'ALOAD_2', 'ALOAD_3', 'IALOAD',
           'LALOAD', 'FALOAD', 'DALOAD', 'AALOAD', 'BALOAD', 'CALOAD']
